import { _decorator, Animation, Component } from "cc";
import { EntityTypeEnum } from "../Common";
import { FsmParamTypeEnum } from "../Enum";
const { ccclass } = _decorator;
import State from "./State";
import SubStateMachine from "./SubStateMachine";

type ParamsValueType = boolean | number;
//每个状态机的参数都有类型和值
export interface IParamsValue {
  type: FsmParamTypeEnum;
  value: ParamsValueType;
}
/**
 * 这个函数是用来减少重复代码的，在setParams()的时候，
 * IParamsValue的type为trigger时，他的value都是固定的，所以直接写一个函数来返回这个值就行了
 */
export const getInitParamsTrigger = () => {
  return {
    type: FsmParamTypeEnum.Trigger,
    value: false,
  };
};
/**
 *  同上
 */
export const getInitParamsNumber = () => {
  return {
    type: FsmParamTypeEnum.Number,
    value: 0,
  };
};

/***
 * 流动图
 * 1.entity的state或者direction改变触发setter
 * 2.setter里触发fsm的setParams方法
 * 3.setParams执行run方法（run方法由子类重写）
 * 4.run方法会更改currentState，然后触发currentState的setter
 * 5-1.如果currentState是子状态机，继续执行他的run方法，run方法又会设置子状态机的currentState，触发子状态run方法
 * 5-2.如果是子状态，run方法就是播放动画
 */

/***
 * 有限状态机基类
 */
@ccclass("StateMachine")
export default abstract class StateMachine extends Component {
  private _currentState: State | SubStateMachine = null; //当前状态
  params: Map<string, IParamsValue> = new Map(); //状态机参数列表
  stateMachines: Map<string, SubStateMachine | State> = new Map(); //状态机列表，值为子状态机或者state，每个state都具有播放动画的能力
  animationComponent: Animation;
  type: EntityTypeEnum;

  getParams(paramName: string) {
    if (this.params.has(paramName)) {
      return this.params.get(paramName).value;
    }
  }
  /**
   * set时执行其中由子类重写的run()进行两个状态之间的条件判断，使用状态机时只需要调用这个方法即可
   */
  setParams(paramName: string, value: ParamsValueType) {
    if (this.params.has(paramName)) {
      this.params.get(paramName).value = value;
      this.run();
      this.resetTrigger();
    }
  }
  //在改变当前状态的时候需要state去播放动画，所以写好getset方法，在set的时候直接播放动画
  get currentState() {
    return this._currentState;
  }

  set currentState(newState) {
    if (!newState) {
      return;
    }
    this._currentState = newState;
    //如果newState的参数是state，就直接调用state的run方法播放动画了
    this._currentState.run();
  }

  /***
   * 清空所有trigger
   */
  resetTrigger() {
    for (const [, value] of this.params) {
      if (value.type === FsmParamTypeEnum.Trigger) {
        value.value = false;
      }
    }
  }

  /***
   * 由子类重写，方法目标是根据当前状态和参数修改currentState
   */
  abstract init(...args: any[]): void;
  abstract run(): void;
}
